/*
 *  Copyright 2010 Alexey Andreev.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package org.xthl.core;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.nop.core.Application;
import org.nop.core.Themer;
import org.nop.util.FinallyUtil;
import org.xthl.core.styling.CssItem;
import org.xthl.core.styling.CssItemInfo;
import org.xthl.core.styling.ScriptInfo;
import org.xthl.core.styling.StylerInfo;
import org.xthl.core.styling.StylerParser;
import org.xthl.core.styling.ViewStyleInfo;

/**
 *
 * @author Alexey Andreev
 */
class StylerFactory {
    private StylerParser parser;
    private Themer themer;
    private Application app;

    public StylerFactory(Application app) {
        this.parser = new StylerParser();
        this.app = app;
        this.themer = app.getThemer();
    }

    public Styler createStyler(Class<?> baseClass, String resourceName) {
        resourceName = "/" + baseClass.getPackage().getName().replace('.', '/') + "/" + 
                resourceName;
        InputStream input = null;
        StylerInfo info;
        try {
            byte[] content = themer.getFile(resourceName);
            input = content == null ? baseClass.getResourceAsStream(resourceName) :
                    new ByteArrayInputStream(content);
            info = parser.parse(input);
        } finally {
            FinallyUtil.close(input);
        }
        final Map<Class<?>, ViewStyleInfo> supportedViews = new HashMap<Class<?>, ViewStyleInfo>();
        for (ViewStyleInfo viewInfo : info.getViewStyles()) {
            for (Class<?> viewType : viewInfo.getViewTypes()) {
                supportedViews.put(viewType, viewInfo);
            }
        }
        Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[] { Styler.class }, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("getSupportedViews")) {
                    Set<Class<?>> result = new HashSet<Class<?>>();
                    result.addAll(supportedViews.keySet());
                    return result;
                } else if (method.getName().equals("getStyles")) {
                    Set<CssItem> result = new HashSet<CssItem>();
                    Class<?> viewType = (Class<?>)args[0];
                    ViewStyleInfo viewInfo = supportedViews.get(viewType);
                    for (CssItemInfo itemInfo : viewInfo.getCssItems()) {
                        CssItem item = new CssItem();
                        Object route = app.get(itemInfo.getMethod().getDeclaringClass());
                        item.setUrl((String)itemInfo.getMethod().invoke(route,
                                itemInfo.getParameters().toArray()));
                        item.setMedia(itemInfo.getMedia());
                        result.add(item);
                    }
                    return result;
                } else if (method.getName().equals("getScripts")) {
                    Set<String> result = new HashSet<String>();
                    Class<?> viewType = (Class<?>)args[0];
                    ViewStyleInfo viewInfo = supportedViews.get(viewType);
                    for (ScriptInfo itemInfo : viewInfo.getScripts()) {
                        Object route = app.get(itemInfo.getMethod().getDeclaringClass());
                        result.add((String)itemInfo.getMethod().invoke(route,
                                itemInfo.getParameters().toArray()));
                    }
                    return result;
                } else if (method.getName().equals("getDependantTypes")) {
                    Set<Class<?>> result = new HashSet<Class<?>>();
                    Class<?> viewType = (Class<?>)args[0];
                    ViewStyleInfo viewInfo = supportedViews.get(viewType);
                    result.addAll(viewInfo.getDependantTypes());
                    return result;
                } else {
                    throw new RuntimeException();
                }
            }
        });
        return (Styler)proxy;
    }
}
